package interview.a1b2c3;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: 李德才
 * @description:
 * @create: 2020-12-19 12:12
 **/


/**
 * 两个线程分别输出 A-Z 和 1-26 ，
 * 要求两个线程交替输出
 * 即：A1B2C3D4
 */
public class Test_04_Lock_Condition {

    public static final String[] LETTER_ARRAY = new String[]{"A", "B", "C", "D", "E", "F"};
    public static final String[] NUMBER_ARRAY = new String[]{"1", "2", "3", "4", "5", "6"};
    private static Lock lock = new ReentrantLock();
    /**
     * 注意，这里是两个Condition，两个Condition来自同一个 ReentrantLock
     */
    private static Condition condition_1 = lock.newCondition();
    private static Condition condition_2 = lock.newCondition();
    private static volatile boolean flag = false;


    /**
     * condition_1.await() 方法会释放当线程中的锁，允许其他线程占用锁
     */

    public static void conditionDemo() {
        /**
         * 注意方法中两个线程使用的是用一把锁
         */
        new Thread(() -> {
//            1、上锁
            lock.lock();
            try {
//                2、条件满足，进入循环
//                8、条件不满足，跳出循环
                while (!flag) {
                    System.out.println(Thread.currentThread().getName() + "当前条件不满足等待");
                    try {
                        /**
                         * 3、condition_1 进入等待状态，
                         * ******* 且 await 方法会释放当前线程中占用的锁 ********* 允许其他线程抢锁
                         */
                        condition_1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
//                9、输出接到通知
                System.out.println(Thread.currentThread().getName() + "接收到通知条件满足");
            } finally {
//                10、释放锁
                lock.unlock();
            }
        }, "消费者").start();

        new Thread(() -> {
//            4、消费者线程释放锁，当前线程可以上锁
            lock.lock();
            try {
//                5、改变状态值
                flag = true;
//                6、唤醒所有 Condition
                condition_1.signalAll();
            } finally {
//                7、释放锁
                lock.unlock();
            }
        }, "生产者").start();
    }


    public static void A1B2C3() {
        /**
         * 这种方式与 synchronized wait notify 相同
         * 但这中方式可以控制哪一种队列启动
         */


        /**
         * 其实是使用 condition.await() 方法控制锁的释放，也就是可以让另一个线程获取到锁
         */
        new Thread(() -> {
            try {
//                1、上锁
                lock.lock();
//                2、循环数组
                for (String str : LETTER_ARRAY) {
//                    3、输出一个元素
                    System.err.println(str);
//                    4、唤醒 condition_2
                    condition_2.signal();
//                    5、condition_1 进入等待状态（注意：await() 方法会释放当前锁）
                    condition_1.await();
                    System.err.println("condition_1.await()");
                    System.err.println("我等等--数字--线程");
                }
                condition_2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "字母").start();

        new Thread(() -> {
            try {
                Thread.sleep(5000);
//                6、字母线程释放锁，当前数字线程可以上锁
                lock.lock();
//                7、循环数字线程
                for (String str : NUMBER_ARRAY) {
//                    8、输出一个元素
                    System.err.println(str);
//                    9、condition_1 被唤醒
                    condition_1.signal();
//                    10、condition_2 进入等待状态（注意：await() 方法会释放当前锁）
                    condition_2.await();
                    System.err.println("我等等--字母--线程");
                }
                condition_1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "数字").start();
    }


    public static void main(String[] args) {
//        conditionDemo();
        A1B2C3();
    }


}
